/**
 * 
 */
package stc.skymobi.zookeeper;

import java.util.ArrayList;
import java.util.List;
import java.util.UUID;

import org.apache.zookeeper.KeeperException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import stc.skymobi.distributed.event.LockResult;
import stc.skymobi.distributed.event.UUIDLockResult;
import stc.skymobi.ebus.EventBus;
import stc.skymobi.ebus.actors.AbstractEventActor;


/**
 * @author hp
 *
 */
public class AsyncMutexService extends AbstractEventActor {
	
    private static final Logger logger = LoggerFactory.getLogger(AsyncMutexService.class);
    
	private	GlobalMutex	mutex;
	private	String		eventLockResult;
	private	String		eventLockAcquire;
	private	String		eventLockRelease;
    private List<Runnable>	dtors = new ArrayList<Runnable>();
	
	private class Callback implements GlobalMutex.LockCallback {

		private	UUID	id;
		private	String	resName;
		private	boolean	acquired = false;
		
		public Callback(UUID id, String resName) {
			this.id = id;
			this.resName = resName;
		}
		
		public void exceptionCaught(Throwable e) {
			UUIDLockResult rslt = new UUIDLockResult();
			
			rslt.setResName(resName)
				.setException(e)
				.setResult(LockResult.LOCK_FAILED)
				.setIdentification(id);
			
			getEventBus().fireEvent(eventLockResult, rslt);
			
		}

		public synchronized void lockAcquired(String lockedId) {
			UUIDLockResult rslt = new UUIDLockResult();
			
			rslt.setResName(resName)
				.setResult(LockResult.LOCK_ACQUIRED)
				.setLockedId(lockedId)
				.setIdentification(id);
			
			getEventBus().fireEvent(eventLockResult, rslt);
			
			acquired = true;
		}

		public synchronized void lockBlocked(String lockedId) {
			if ( !acquired ) {
				UUIDLockResult rslt = new UUIDLockResult();
				
				rslt.setResName(resName)
					.setResult(LockResult.LOCK_BLOCKED)
					.setLockedId(lockedId)
					.setIdentification(id);
				
				getEventBus().fireEvent(eventLockResult, rslt);
			}
		}
		
	}
	
	public AsyncMutexService(EventBus bus, String name, String root, ZooKeeperOperations zk) 
		throws KeeperException, InterruptedException {
		super(bus, name);
		mutex = new GlobalMutex(root, zk);
	}
	
	@SuppressWarnings("unused")
	private void acquireLock(UUID id, String resName) {
		mutex.acquireLock(resName, new Callback(id, resName) );
	}
	
	@SuppressWarnings("unused")
	private void releaseLock(String lockedId) {
		mutex.releaseLock(lockedId);
	}
	
	public void start() {
		dtors.add( this.registerObserver(eventLockAcquire, this, "acquireLock"));
		dtors.add( this.registerObserver(eventLockRelease, this, "releaseLock"));
	}

	public void destroy() {
    	for ( Runnable canceler : dtors ) {
	        try {
	        	canceler.run();
	        } catch (Exception e) {
	        	logger.error("destroy:", e);
	        }
    	}
    	dtors.clear();
        
        super.destroy();
	}

	/**
	 * @return the eventLockResult
	 */
	public String getEventLockResult() {
		return eventLockResult;
	}

	/**
	 * @param eventLockResult the eventLockResult to set
	 */
	public void setEventLockResult(String eventLockResult) {
		this.eventLockResult = eventLockResult;
	}

	/**
	 * @return the eventLockAcquire
	 */
	public String getEventLockAcquire() {
		return eventLockAcquire;
	}

	/**
	 * @param eventLockAcquire the eventLockAcquire to set
	 */
	public void setEventLockAcquire(String eventLockAcquire) {
		this.eventLockAcquire = eventLockAcquire;
	}

	/**
	 * @return the eventLockRelease
	 */
	public String getEventLockRelease() {
		return eventLockRelease;
	}

	/**
	 * @param eventLockRelease the eventLockRelease to set
	 */
	public void setEventLockRelease(String eventLockRelease) {
		this.eventLockRelease = eventLockRelease;
	}
}
